﻿Imports Org.BouncyCastle.Crypto.Parameters
Imports iTextSharp.text.pdf
Imports Org.BouncyCastle.X509
Imports System.IO
Imports Org.BouncyCastle.Tsp
Imports Org.BouncyCastle.Ocsp
Imports SharedTools
Imports iTextSharp.text

Public Class PDFManager
    Public Shared Function SignPDF(original As PDFItem) As PDFItem
        Dim item = original.Copy
        item.PDFBytes = SignPDF(item.PDFBytes)
        item.IsSigned = True
        Return item
    End Function

    Public Shared Function SignPDF(filePath As String) As Byte()
        Return SignPDF(File.ReadAllBytes(filePath))
    End Function

    Public Shared Function SignPDF(pdfBytes As Byte()) As Byte()

        Dim outmemstream As New CloseHijackedMemoryStream()

        Dim p = Repository.CurrentProfile
        Dim prvKey = DirectCast(p.SigCert.CertificateKey(Repository.CurrentPassword).Private, RsaPrivateCrtKeyParameters)
        Dim sigCert = p.SigCert.Certificate

        Dim reader As New PdfReader(pdfBytes)
        Dim stp As PdfStamper = PdfStamper.CreateSignature(reader, outmemstream, ControlChars.NullChar)
        Dim sap As PdfSignatureAppearance = stp.SignatureAppearance
        sap.SetCrypto(prvKey, New X509Certificate() {sigCert, Repository.CAcertificate}, Nothing, PdfSignatureAppearance.SELF_SIGNED)
        sap.Reason = "Just to proove I can sign!"
        sap.Location = p.Country

        ' comment next line to have an invisible signature
        stp.Close()
        Dim bytest = outmemstream.ToArray
        outmemstream.RealClose()

        Return bytest
    End Function

    Public Shared Function SignAndEncryptPDF(original As PDFItem, cert As X509Certificate, algorithm As Integer) As PDFItem
        Dim item = original.Copy
        item.PDFBytes = SignAndEncryptPDF(item.PDFBytes, cert, algorithm)
        item.IsSigned = True
        item.IsEncrypted = True
        Return item
    End Function

    Public Shared Function SignAndEncryptPDF(pdfBytes As Byte(), cert As X509Certificate, algorithm As Integer) As Byte()

        Dim outmemstream As New CloseHijackedMemoryStream()

        Dim p = Repository.CurrentProfile
        Dim prvKey = DirectCast(p.SigCert.CertificateKey(Repository.CurrentPassword).Private, RsaPrivateCrtKeyParameters)
        Dim sigCert = p.SigCert.Certificate

        Dim reader As New PdfReader(pdfBytes)
        Dim stp As PdfStamper = PdfStamper.CreateSignature(reader, outmemstream, ControlChars.NullChar)
        Dim sap As PdfSignatureAppearance = stp.SignatureAppearance

        stp.SetEncryption({cert}, {PdfWriter.ALLOW_COPY}, algorithm)

        sap.SetCrypto(prvKey, New X509Certificate() {sigCert, Repository.CAcertificate}, Nothing, PdfSignatureAppearance.SELF_SIGNED)
        sap.Reason = "Just to proove I can sign!"
        sap.Location = p.Country
        stp.Close()
        Dim bytest = outmemstream.ToArray
        outmemstream.RealClose()





        Return bytest
    End Function

    Public Shared Function VerifyPDFSignature(filePath As String, Optional cert As X509Certificate = Nothing, Optional prvKey As RsaPrivateCrtKeyParameters = Nothing) As PDFVerificationResponse
        Return VerifyPDFSignature(File.ReadAllBytes(filePath), cert, prvKey)
    End Function

    Public Shared Function VerifyPDFSignature(pdfBytes As Byte(), Optional cert As X509Certificate = Nothing, Optional prvKey As RsaPrivateCrtKeyParameters = Nothing) As PDFVerificationResponse
        Dim reader As PdfReader
        Dim tmpfile = String.Empty
        If (cert IsNot Nothing AndAlso prvKey IsNot Nothing) Then
            tmpfile = Path.GetTempFileName
            File.WriteAllBytes(tmpfile, pdfBytes)
            reader = New PdfReader(tmpfile, cert, prvKey)
        Else
            reader = New PdfReader(pdfBytes)
        End If

        Dim af As AcroFields = reader.AcroFields
        Dim name = af.GetSignatureNames()(0)

        Dim pk As PdfPKCS7 = af.VerifySignature(name)
        Dim signatureTimestamp As DateTime = pk.SignDate
        Dim authorSigCert As X509Certificate() = pk.SignCertificateChain

        'check if the file has been tampered with
        If Not pk.Verify Then
            Return PDFVerificationResponse.Modified
        End If

        'check if the certificate used to sign was expired at the time of signing
        If authorSigCert(0).NotAfter < signatureTimestamp Then
            Return PDFVerificationResponse.CertificateExpiredBeforeSigning
        End If

        'checks if the certificate was revoked at the time of signing
        Dim ocspresult = CRLManager.CheckCertificateAgainstOCSP(authorSigCert(0))
        If ocspresult.Item1 = SharedTools.OCSPQueryResponse.Revoked AndAlso ocspresult.Item2 < signatureTimestamp Then
            Return PDFVerificationResponse.CertificateRevokedBeforeSigning
        End If

        'all checks passed, signature is valid
        Return PDFVerificationResponse.Valid
    End Function

    Public Shared Function IsEncrypted(pdfBytes As Byte(), cert As X509Certificate, prvKey As RsaPrivateCrtKeyParameters) As Boolean
        Dim tempFile = Path.GetTempFileName
        File.WriteAllBytes(tempFile, pdfBytes)

        Dim reader As New PdfReader(tempFile, cert, prvKey)

        Dim result = reader.IsEncrypted
        reader.Close()
        File.Delete(tempFile)
        Return result
    End Function

    Public Shared Function IsEncrypted(item As PDFItem, cert As X509Certificate, prvKey As RsaPrivateCrtKeyParameters) As Boolean
        Return IsEncrypted(item.PDFBytes, cert, prvKey)
    End Function

    Public Shared Function EncryptPDF(bytes As Byte(), cert As X509Certificate, algorithm As Integer) As Byte()
        Dim outmemstream As New CloseHijackedMemoryStream()

        Dim reader As New PdfReader(bytes)

        Dim stp As New PdfStamper(reader, outmemstream)


        stp.SetEncryption(New X509Certificate() {cert}, New Integer() {PdfWriter.ALLOW_COPY}, algorithm)
        stp.Close()

        Dim outbytes = outmemstream.ToArray
        outmemstream.RealClose()

        Return outbytes
    End Function

    Public Shared Function EncryptPDF(original As PDFItem, cert As X509Certificate, algorithm As Integer) As PDFItem
        Dim item = original.Copy
        item.PDFBytes = EncryptPDF(item.PDFBytes, cert, algorithm)
        item.IsEncrypted = True
        Return item
    End Function

    Public Shared Function DecryptPDF(pdfBytes As Byte(), cert As X509Certificate, prvKey As RsaPrivateCrtKeyParameters) As Byte()
        'Trick to load the PdfReader, since apparently the constructor that accepts a certificate and key doesn't accept a ByteArray as input
        Dim tempFile = Path.GetTempFileName
        File.WriteAllBytes(tempFile, pdfBytes)
        Dim reader As PdfReader = Nothing

        Try
            reader = New PdfReader(tempFile, cert, prvKey)
        Catch ex As Exception
            Return Nothing
        End Try

        Dim outmemstream As New CloseHijackedMemoryStream()

        Dim stamper As New PdfStamper(reader, outmemstream)
        stamper.Close()
        reader.Close()

        'MemoryStream now contains decoded PDF
        File.Delete(tempFile)
        Return outmemstream.ToArray
    End Function

    Public Shared Function DecryptPDF(original As PDFItem, cert As X509Certificate, prvKey As RsaPrivateCrtKeyParameters) As PDFItem
        Dim item = original.Copy
        item.PDFBytes = DecryptPDF(item.PDFBytes, cert, prvKey)
        item.IsEncrypted = False
        Return item
    End Function

    Public Shared Function DecryptPDF(pdfBytes As Byte(), certs As X509Certificate(), prvKeys As RsaPrivateCrtKeyParameters()) As Byte()
        If certs.Count <> prvKeys.Count Then
            Throw New ArgumentException("You must provide exactly one key for each certificate")
        End If

        For i As Integer = 0 To certs.Count - 1
            Dim bytes = DecryptPDF(pdfBytes, certs(i), prvKeys(i))
            If bytes IsNot Nothing Then
                Return bytes
            End If
        Next
        Return Nothing
    End Function

    Public Shared Function DecryptPDF(original As PDFItem, certs As X509Certificate(), prvKeys As RsaPrivateCrtKeyParameters()) As PDFItem
        If certs.Count <> prvKeys.Count Then
            Throw New ArgumentException("You must provide exactly one key for each certificate")
        End If

        Dim item = original.Copy
        For i As Integer = 0 To certs.Count - 1
            Dim bytes = DecryptPDF(item.PDFBytes, certs(i), prvKeys(i))
            If bytes IsNot Nothing Then
                item.PDFBytes = bytes
                item.IsEncrypted = False
                Return item
            End If
        Next
        Return Nothing
    End Function

    Shared Function PDFIncoming(p As PDFItem) As Boolean
        Repository.PDFDocuments.Add(p)
        RaiseEvent PDFReceived(p)
        Return True
    End Function

    Public Shared Event PDFReceived(p As PDFItem)

    Shared Function CreateItem(filename As String) As PDFItem
        Dim item = New PDFItem
        item.Username = Repository.CurrentProfile.UserName
        item.Title = filename.Split(CChar("\")).Last
        item.PDFBytes = File.ReadAllBytes(filename)
        item.IsEncrypted = False
        Return item
    End Function

    Shared Function SendPDF(item As PDFItem, recipient As DiscoveredClientItem) As Boolean
        Return recipient.Client.SendPDF(ConvertPDFItem(item))
    End Function

    Private Shared Function ConvertPDFItem(item As ProgettoSicurezza.Client.PDFItem) As ProgettoSicurezza.Client.ClientServices.PDFItem
        Dim newitem As New ProgettoSicurezza.Client.ClientServices.PDFItem
        newitem.IsEncrypted = item.IsEncrypted
        newitem.IsSigned = item.IsSigned
        newitem.Title = item.Title
        newitem.PDFBytes = item.PDFBytes
        newitem.Username = item.Username
        Return newitem
    End Function
End Class
